
package com.huilan.base.esl.service.aop;

import cn.hutool.core.date.TimeInterval;
import com.alibaba.fastjson.JSON;
import com.huilan.base.esl.pojo.ApiResult;
import com.huilan.base.esl.pojo.ApiResultBuild;
import com.huilan.base.esl.service.exeception.TimeoutException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.sql.Time;
import java.util.HashMap;

@Aspect
@Slf4j
@Component
public class ApiLogAspect {

    /**
     * AOP通知：
     * 1. 前置通知：在方法调用之前执行
     * 2. 后置通知：在方法正常调用之后执行
     * 3. 环绕通知：在方法调用之前和之后，都分别可以执行的通知
     * 4. 异常通知：如果在方法调用过程中发生异常，则通知
     * 5. 最终通知：在方法调用之后执行
     */

    /**
     * 切面表达式：
     * execution 代表所要执行的表达式主体
     * 第一处 * 代表方法返回类型 *代表所有类型
     * 第二处 包名代表aop监控的类所在的包
     * 第三处 .. 代表该包以及其子包下的所有类方法
     * 第四处 * 代表类名，*代表所有类
     * 第五处 *(..) *代表类中的方法名，(..)表示方法中的任何参数
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(com.huilan.base.esl.pojo.ApiResult com.huilan.base.esl.service.impl..*.*(..)) ")
    public Object recordTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object proceed;
        TimeInterval interval = new TimeInterval();
        interval.start();
        String methodName = joinPoint.getSignature().getName();
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String remoteHost = getIpAddress(request);
            String params = getParams(joinPoint,request);
            log.info("------START------ method:{} ip:{} params:{}", methodName, remoteHost,params);
            proceed = joinPoint.proceed();
            log.info("------END-------- method:{} executed:{}ms result:{} ", methodName, interval.intervalMs(), JSON.toJSONString(proceed));
        }catch (TimeoutException t){
            ApiResult fail = ApiResultBuild.fail(501, t.getMsg());
            log.info("------END-------- method:{} executed:{}ms result:{} ", methodName, interval.intervalMs(), JSON.toJSONString(fail));
            return fail;
        }catch (Exception e){
            log.error("Api Exception",e);
            ApiResult apiResult = ApiResultBuild.fail("系统繁忙");
            log.info("------END-------- method:{} executed:{}ms result:{} ", methodName, interval.intervalMs(), JSON.toJSONString(apiResult));
            return apiResult;
        }
        return proceed;
    }

    public static String getParams(ProceedingJoinPoint joinPoint,HttpServletRequest request){
        String params = "";
        try {
            String method = request.getMethod();
            Object[] args = joinPoint.getArgs();
            //获取请求参数集合并进行遍历拼接
            if (args.length > 0) {
                if ("POST".equals(method)) {
                    MethodSignature ms = (MethodSignature) joinPoint.getSignature();
                    String[] parameterNames = ms.getParameterNames();
                    if (parameterNames != null && parameterNames.length > 0) {
                        HashMap<String, Object> map = new HashMap<>();
                        for (int i = 0; i < parameterNames.length; i++) {
                            map.put(parameterNames[i], args[i]);
                        }
                        params = JSON.toJSONString(map);
                    }
                } else if ("GET".equals(method)) {
                    params = request.getQueryString();
                }
            }
        }catch (Exception e){
            log.error("log记录请求参数异常,不影响业务",e);
        }
        return params;
    }

    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }


}
